package com.bookbuf.wechat;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import com.bookbuf.social.PlatformEnum;
import com.bookbuf.social.exceptions.SocializeException;
import com.bookbuf.social.handlers.IShareHandler;
import com.bookbuf.social.handlers.SSOHandler;
import com.bookbuf.social.handlers.share.content.ShareContent;
import com.bookbuf.social.platforms.WeChatPlatform;
import com.bookbuf.social.util.Dummy;
import com.bookbuf.social.util.QueuedWork;
import com.bookbuf.social.util.Util;
import com.bookbuf.wechat.content.WXShareContent;
import com.bookbuf.wechat.localize.WXPreferences;
import com.bookbuf.wechat.util.WXAuthUtils;
import com.tencent.mm.sdk.openapi.BaseReq;
import com.tencent.mm.sdk.openapi.BaseResp;
import com.tencent.mm.sdk.openapi.IWXAPI;
import com.tencent.mm.sdk.openapi.IWXAPIEventHandler;
import com.tencent.mm.sdk.openapi.SendAuth;
import com.tencent.mm.sdk.openapi.SendMessageToWX;
import com.tencent.mm.sdk.openapi.WXAPIFactory;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import static com.bookbuf.social.PlatformEnum.WX_SCENE;

// TODO: 16/6/29 接入微信第三方登陆

public class WXSSOHandler extends SSOHandler<WeChatPlatform> {

	private static final String TAG = "WXSSOHandler";

	private PlatformEnum mTarget;
	private boolean isToCircle;

	private WXShareContent shareContent;

	// --------------------------------------
	// ----------- 持久化      ------------
	// --------------------------------------

	private WXPreferences mPreference;

	// --------------------------------------
	// ----------- 监听器      ------------
	// --------------------------------------

	private AuthListener mAuthListener;
	private ShareListener mShareListener;

	// --------------------------------------
	// ----------- 微信提供的接口 ------------
	// --------------------------------------

	private static final int REQUEST_CODE_WX = 10086;
	private static final int REQUEST_CODE_WX_CIRCLE = REQUEST_CODE_WX + 1;

	private static final int REFRESH_TOKEN_EXPIRES = 604800;
	private static final int RESP_TYPE_AUTH = 1;
	private static final int RESP_TYPE_SHARE = 2;

	private static String sState = "none";
	private static String sScope = "snsapi_userinfo,snsapi_friend,snsapi_message";
	private IWXAPI mWXApi;
	private IWXAPIEventHandler mEventHandler;


	/**
	 * <p>type 1 = {@link com.tencent.mm.sdk.openapi.SendAuth.Resp#getType()};</p>
	 * <p>type 2 = {@link com.tencent.mm.sdk.openapi.SendMessageToWX.Resp#getType()};</p>
	 * <p>type 3 = {@link com.tencent.mm.sdk.openapi.GetMessageFromWX.Resp#getType()};</p>
	 * <p>type 4 = {@link com.tencent.mm.sdk.openapi.ShowMessageFromWX.Resp#getType()};</p>
	 */
	public WXSSOHandler () {
		this.mTarget = WX_SCENE;
		this.isToCircle = false;
		this.mEventHandler = new IWXAPIEventHandler () {
			@Override
			public void onReq (BaseReq baseReq) {

			}

			@Override
			public void onResp (BaseResp resp) {
				final int type = resp.getType ();
				Log.d (TAG, "onResp: type = " + type);
				switch (type) {
					case RESP_TYPE_AUTH:
						WXSSOHandler.this.onAuthCallback ((SendAuth.Resp) resp);
						break;
					case RESP_TYPE_SHARE:
						WXSSOHandler.this.onShareCallback ((SendMessageToWX.Resp) resp);
						break;
					default:
						break;
				}
			}
		};

	}

	public IWXAPIEventHandler getWXEventHandler () {
		return this.mEventHandler;
	}

	public IWXAPI getWXApi () {
		return this.mWXApi;
	}

	public void setScope (String... scope) {
		sScope = TextUtils.join (",", scope);
	}

	@Override
	public void onCreate (Context context, WeChatPlatform platform) {
		this.mPreference = new WXPreferences (context.getApplicationContext (), "service_wx");
		this.setContext (context);
		this.setPlatform (platform);
		final String appId = getPlatform ().appId;
		this.mWXApi = WXAPIFactory.createWXAPI (context.getApplicationContext (), appId);
		this.mWXApi.registerApp (appId);
		if (!isInstalled (context)) {
			StringBuilder builder = new StringBuilder ();
			builder.append ("请安装");
			builder.append (this.mTarget);
			builder.append ("客户端");
			Toast.makeText (context, builder.toString (), Toast.LENGTH_SHORT).show ();
		}
	}

	@Override
	public int getRequestCode () {
		return isToCircle ? REQUEST_CODE_WX_CIRCLE : REQUEST_CODE_WX;
	}

	@Override
	public void callApiProfile (Activity activity, final AuthListener listener) {
		String uid = mPreference.getUID ();
		String accessToken = mPreference.getAccessToken ();
		if (!TextUtils.isEmpty (uid) && !TextUtils.isEmpty (accessToken)) {
			if (!mPreference.isAccessTokenAvailable ()) {
				String refreshToken = mPreference.getRefreshToken ();
				getAuthWithRefreshToken (refreshToken, true);
			}
			StringBuilder builder = new StringBuilder ();
			builder.append ("https://api.weixin.qq.com/sns/userinfo?access_token=");
			builder.append (accessToken).append ("&openid=").append (uid);
			builder.append ("&lang=zh_CN");
			String jsonStr = WXAuthUtils.request (builder.toString ());
			final Map map = this.parseUserInfo (jsonStr);
			QueuedWork.runInMain (new Runnable () {
				public void run () {
					listener.onComplete (WX_SCENE, AuthListener.ACTION_GET_PROFILE, map);
				}
			});
		} else {
			QueuedWork.runInMain (new Runnable () {
				@Override
				public void run () {
					listener.onComplete (WX_SCENE, AuthListener.ACTION_GET_PROFILE, null);
				}
			});
		}


	}

	@Override
	public void callApiCheckAccessTokenValid (Activity activity, final AuthListener listener) {
		String accessToken = mPreference.getAccessToken ();
		String uid = mPreference.getUID ();
		final String url = "https://api.weixin.qq.com/sns/auth?access_token=" + accessToken + "&openid=" + uid;
		Thread thread = new Thread (new Runnable () {
			public void run () {
				String result = WXAuthUtils.request (url);

				try {
					final JSONObject jsonObject = new JSONObject (result);
					if (jsonObject != null) {
						QueuedWork.runInMain (new Runnable () {
							public void run () {
								HashMap map = new HashMap ();
								if (jsonObject.optInt ("errcode", -1) == BaseResp.ErrCode.ERR_OK) {
									map.put ("result", jsonObject.toString ());
									listener.onComplete (PlatformEnum.WX_SCENE, AuthListener.ACTION_GET_PROFILE, map);
								} else {
									listener.onError (PlatformEnum.WX_SCENE, AuthListener.ACTION_GET_PROFILE, new Throwable (jsonObject.toString ()));
								}

							}
						});
					}
				} catch (JSONException var3) {
					var3.printStackTrace ();
				}

			}
		});
		thread.start ();
	}

	private Map<String, String> parseUserInfo (String result) {
		HashMap map = new HashMap ();

		try {
			JSONObject e = new JSONObject (result);
			boolean error = e.has ("errcode");
			if (error) {
				map.put ("errcode", e.getString ("errcode"));
				return map;
			} else {
				map.put ("openid", e.opt ("openid").toString ());
				map.put ("nickname", e.opt ("nickname").toString ());
				map.put ("language", e.opt ("language").toString ());
				map.put ("city", e.opt ("city").toString ());
				map.put ("province", e.opt ("province").toString ());
				map.put ("country", e.opt ("country").toString ());
				map.put ("headimgurl", e.opt ("headimgurl").toString ());
				map.put ("unionid", e.opt ("unionid").toString ());
				map.put ("sex", e.opt ("sex").toString ());
				JSONArray jsonArray = e.getJSONArray ("privilege");
				int len = jsonArray.length ();
				if (len > 0) {
					String[] privileges = new String[len];

					for (int i = 0; i < len; ++i) {
						privileges[i] = jsonArray.get (i).toString ();
					}

					map.put ("privilege", privileges.toString ());
				}

				return map;
			}
		} catch (JSONException var9) {
			var9.printStackTrace ();
		}
		return Collections.emptyMap ();
	}

	@Override
	public boolean share (Activity activity, ShareContent shareContent, IShareHandler.ShareListener shareListener) {

		if (activity == null) {
			Log.w (TAG, "share: activity is null");
			return false;
		} else {
			if (isInstalled (activity)) {
				this.shareContent = new WXShareContent.WXShareContentWrapper (shareContent).build ();
				WXShareContent content = this.shareContent;
				if (checkShareAction (content)) {
					this.mShareListener = shareListener;
					return shareImpl (content);
				} else {
					Toast.makeText (activity, getPlatform ().getName () + "不支持分享类型:" + content.getType (), Toast.LENGTH_SHORT).show ();
					return false;
				}

			} else {
				Toast.makeText (activity, "你还没有安装微信", Toast.LENGTH_SHORT).show ();
				return false;
			}
		}
	}

	// 注意 微信朋友圈不支持表情分享
	private boolean checkShareAction (WXShareContent content) {
		return true;
	}

	/**
	 * 分享或收藏的目标场景，通过修改scene场景值实现。
	 * 发送到聊天界面——WXSceneSession
	 * 发送到朋友圈——WXSceneTimeline
	 * 添加到微信收藏——WXSceneFavorite(微信似乎不开放这个接口了?接入文档没有任何说明?)
	 */
	private boolean shareImpl (WXShareContent content) {

		SendMessageToWX.Req req = new SendMessageToWX.Req ();
		req.transaction = WXAuthUtils.APIParamUtil.buildTransaction (content.getType ().getValue ());
		req.message = content.build ();


		final int WXSceneFavorite = 2;
		PlatformEnum enumValue = getPlatform ().getName ();
		switch (enumValue) {
			case WX_SCENE:
				req.scene = SendMessageToWX.Req.WXSceneSession;
				break;
			case WX_SCENE_TIMELINE:
				req.scene = SendMessageToWX.Req.WXSceneTimeline;
				break;
			case WX_SCENE_FAVORITE:
				req.scene = WXSceneFavorite;
				break;
			default:
				break;

		}
		return mWXApi.sendReq (req);
	}

	protected void onShareCallback (SendMessageToWX.Resp resp) {
		switch (resp.errCode) {
			case BaseResp.ErrCode.ERR_SENT_FAILED:
			case BaseResp.ErrCode.ERR_COMM:
				this.mShareListener.onError (this.mTarget, new SocializeException (resp.errCode, resp.errStr));
				break;
			case BaseResp.ErrCode.ERR_USER_CANCEL:
				this.mShareListener.onCancel (this.mTarget);
				break;
			case BaseResp.ErrCode.ERR_OK:
				this.mShareListener.onResult (this.mTarget);
				break;
			default:
				Log.d (TAG, "onShareCallback: resp.errCode = " + resp.errCode);
		}

	}

	@Override
	public boolean isInstalled (Context context) {
		return this.mWXApi.isWXAppInstalled ();
	}

	/**
	 * 刷新access_token有效期
	 * access_token是调用授权关系接口的调用凭证，由于access_token有效期（目前为2个小时）较短，当access_token超时后，可以使用refresh_token进行刷新，access_token刷新结果有两种：
	 * 1. 若access_token已超时，那么进行refresh_token会获取一个新的access_token，新的超时时间；
	 * 2. 若access_token未超时，那么进行refresh_token不会改变access_token，但超时时间会刷新，相当于续期access_token。
	 * refresh_token拥有较长的有效期（30天），当refresh_token失效的后，需要用户重新授权。
	 */
	public void applyAuthorize (Activity activity, AuthListener listener) {
		this.mAuthListener = listener;
		if (mPreference.isAuthValid ()) {
			final String req = mPreference.getRefreshToken ();
			if (mPreference.isAccessTokenAvailable ()) {
				Map map = this.getAuthWithRefreshToken (req, true);
				Log.d (TAG, "applyAuthorize: access_token available, map = " + map);
				this.mAuthListener.onComplete (WX_SCENE, AuthListener.ACTION_AUTHORIZE, map);
			} else {
				Map map = this.getAuthWithRefreshToken (req, true);
				Log.d (TAG, "applyAuthorize: access_token unavailable, map = " + map);
				this.mAuthListener.onComplete (WX_SCENE, AuthListener.ACTION_AUTHORIZE, map);
			}
		} else {
			/* 重新请求 refresh_token*/
			SendAuth.Req req = new SendAuth.Req ();
			req.scope = sScope;
			req.state = sState;
			this.mWXApi.sendReq (req);
		}

	}

	@Override
	public boolean isAuthorized (Context context) {
		return mPreference.isAuth ();
	}

	private Bundle parseAuthData (String response) {
		Bundle bundle = new Bundle ();
		if (TextUtils.isEmpty (response)) {
			return bundle;
		} else {
			try {
				JSONObject jsonObject = new JSONObject (response);
				Iterator iterator = jsonObject.keys ();
				String key;

				while (iterator.hasNext ()) {
					key = (String) iterator.next ();
					bundle.putString (key, jsonObject.optString (key));
				}

				bundle.putString ("uid", bundle.getString ("openid"));
				bundle.putLong ("refresh_token_expires", REFRESH_TOKEN_EXPIRES);
			} catch (JSONException var6) {
				var6.printStackTrace ();
			}
			return bundle;
		}
	}

	protected void onAuthCallback (SendAuth.Resp resp) {
		AuthListener listener = Dummy.get (AuthListener.class, this.mAuthListener);
		switch (resp.errCode) {
			case BaseResp.ErrCode.ERR_OK:
				this.getAuthWithCode (resp.token, listener);
				break;
			case BaseResp.ErrCode.ERR_USER_CANCEL:
				listener.onCancel (WX_SCENE, AuthListener.ACTION_AUTHORIZE);
				break;
			case BaseResp.ErrCode.ERR_COMM:
			case BaseResp.ErrCode.ERR_SENT_FAILED:
			case BaseResp.ErrCode.ERR_AUTH_DENIED:
			case BaseResp.ErrCode.ERR_UNSUPPORT:
			default:
				CharSequence err = TextUtils.concat (
						new CharSequence[]{
								"weixin auth error (",
								String.valueOf (resp.errCode), "):",
								resp.errStr});
				SocializeException ex = new SocializeException (err.toString ());
				listener.onError (WX_SCENE, AuthListener.ACTION_AUTHORIZE, ex);
				break;
		}
	}

	@Override
	public void deleteAuthorize (Context context, AuthListener listener) {
		super.deleteAuthorize (context, listener);
		if (this.isInstalled (context)) {
			this.mPreference.delete ();
			listener.onComplete (WX_SCENE, AuthListener.ACTION_DELETE, null);
		}
	}

	@Override
	public boolean isSupportAuthorize () {
		return true;
	}


	private Map<String, String> getAuthWithRefreshToken (String refresh_token, boolean autoSaveResponse) {
		StringBuilder authURL = new StringBuilder ();
		authURL.append ("https://api.weixin.qq.com/sns/oauth2/refresh_token?");
		authURL.append ("appid=").append (this.getPlatform ().appId);
		authURL.append ("&grant_type=refresh_token");
		authURL.append ("&refresh_token=").append (refresh_token);
		String response = WXAuthUtils.request (authURL.toString ());

		if (autoSaveResponse) {
			Bundle bundle = this.parseAuthData (response);
			this.mPreference.setBundle (bundle);
		}


		Map map = null;

		try {
			map = Util.jsonToMap (response);
		} catch (Exception e) {
			e.printStackTrace ();
		}

		return map;
	}


	/***
	 * 通过code获取access_token
	 */
	private void getAuthWithCode (String code, final AuthListener listener) {
		final StringBuilder authURL = new StringBuilder ();
		authURL.append ("https://api.weixin.qq.com/sns/oauth2/access_token?");
		authURL.append ("appid=").append (getPlatform ().appId);
		authURL.append ("&secret=").append (getPlatform ().appSecret);
		authURL.append ("&code=").append (code);
		authURL.append ("&grant_type=authorization_code");
		(new Thread (new Runnable () {
			public void run () {
				String response = WXAuthUtils.request (authURL.toString ());
				final Map map;

				try {
					Map tmp = Util.jsonToMap (response);
					if (tmp == null || tmp.size () == 0) {
						map = mPreference.getmap ();
					} else {
						map = tmp;
						// save data
						Bundle bundle = WXSSOHandler.this.parseAuthData (response);
						WXSSOHandler.this.mPreference.setBundle (bundle);
					}

					QueuedWork.runInMain (new Runnable () {
						public void run () {
							listener.onComplete (WX_SCENE, AuthListener.ACTION_AUTHORIZE, map);
						}
					});
				} catch (Exception e) {
					e.printStackTrace ();
				}

			}
		})).start ();
	}

}
